package com.appspot.gaeforum309.pathing;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.appspot.gaeforum309.db.DBCategory;
import com.appspot.gaeforum309.db.DBComment;
import com.appspot.gaeforum309.db.DBConversation;
import com.appspot.gaeforum309.db.DBTopic;

// TODO: Les Path devraient pouvoir être placé dans le memcache, aucune raison que le Path vers un objet évolu.

/**
 * Path mechanism represent the hierarchy of objectd, and the path to an object.
 * For example, a comment is in a conversation which is in a topic which is in a category.
 * This is an abstraction of the path : each media output will use it to represent the path to the current element.
 * 
 * Each type of object is identified with a TagName. The tagname can be used to found the type of object.
 * Each instance of object have a KeyStr. KeyStr can be used to retrieve the object from the database.
 * 
 * For example, the path to a conversation can be :
 * Cat(KeyOfTheCat) / Topic(KeyOfTheTopic) / Conversation(KeyOfTheConversation)
 * When you have a conversation(tagname=Conversation and the key of the conversation), 
 * PathBuilder can return to you the previous path.
 */
public final class PathBuilder {
	
	private PathBuilder()
	{
		
	}
	
	private static Map<String, PathableClass> listPathable = new HashMap<String, PathableClass>();

	public abstract interface IPathable
	{
		public String getKeyStr();
		public String getParentKeyStr();
		public PathableClass getPathable();
		public String label();
		// TODO: add a solution to specify the wanted tagname.
		public List<? extends IPathable> getChilds(int fromNumber, int toNumber);
		public List<? extends IPathable> getChilds();
		
		public boolean authorizedAction(Action.ActionType actionType);
		
		public boolean applyAction(Action action);
	}
	
	static boolean init = false;
	
	synchronized private static void init()
	{
		if(init == true)
			return;
		
		listPathable.put(PathRoot.pathable.getTagName(), PathRoot.pathable);
		listPathable.put(DBCategory.pathable.getTagName(), DBCategory.pathable);
		listPathable.put(DBTopic.pathable.getTagName(), DBTopic.pathable);
		listPathable.put(DBConversation.pathable.getTagName(), DBConversation.pathable);
		listPathable.put(DBComment.pathable.getTagName(), DBComment.pathable);
		
		init = true;
	}
	
	/**
	 * This methode will be called by PathableClass instance at his instanciation to insert the new type of
	 * element in the tree.
	 * @param tagName Tag name of the new type of element
	 * @param parentTagName PathableClass of parent, or null for root element.
	 */
	/*public static void declarePathable(String tagName, PathableClass pc)
	{
		listPathable.put(tagName, pc);
	}*/
	public static PathableClass getPathableClass(String tagName)
	{
		init();
		
		return listPathable.get(tagName);
	}
	
	public static Path buildPath (String tagName, String keyStr)
	{
		init();
		
		// Add current object
		List<IPathable> path = new ArrayList<IPathable>();
		PathableClass pc = listPathable.get(tagName);
		
		do
		{
			IPathable iobject = pc.getObject(keyStr);
			path.add(iobject);
			keyStr = iobject.getParentKeyStr();
			tagName = pc.getParentTagName();
			pc=listPathable.get(tagName);
		} while(tagName != null);
		
		return new Path(path);
	}
	
	public static Path pathRoot()
	{
		return buildPath(PathRoot.tagName, PathRoot.tagKey);
	}
	
	public static PathableClass getChildType(String parentTagName)
	{
		if(parentTagName == null)
			return null;
		
		for(PathableClass pc:listPathable.values())
		{
			if(parentTagName.equals(pc.getParentTagName()))
				return pc;
		}
		return null;
	}
}
